<?php

namespace App\Kuafu\Invoice\Service;

use App\Kuafu\Invoice\Enum\InvoiceCodeEnum;
use App\Kuafu\Invoice\Exceptions\InvoiceException;
use GuzzleHttp\Client as GuzzleClient;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;

class MoredoBaseService
{
    #获取盟度token请求链接
    const GET_TOKEN_URL      = "http://api.moredo.cn:9000/web/api/request/getToken";
    const APPLY_INVOICE_URL  = "http://api.moredo.cn:9000/web/api/request/applyInvoice";
    const RED_INVOICE_URL    = "http://api.moredo.cn:9000/web/api/request/redInvoice";
    const RED_INVOICE_V2_URL = "http://api.moredo.cn:9000/web/api/request/applyInvoiceV2";
    const ADD_DEVICE_URL     = "http://api.moredo.cn:9000/web/api/request/addDevice";
    const INIT_URL           = "http://api.moredo.cn:9000/web/api/request/init";
    const CANCEL_INVOICE_URL = "http://api.moredo.cn:9000/web/api/request/cancelInvoice";

    /**
     * 获取盟度token
     * @return mixed
     * @throws InvoiceException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public static function getToken()
    {
        $redisKey = Redis::get(InvoiceCodeEnum::PREFIX_TOKEN . config('moredo.ACCESS_KEY'));
        Log::channel('invoice')->info('盟度-token' . $redisKey);
        if ($redisKey) {
            return $redisKey;
        }
        $data = [
            'accessKey' => config('moredo.ACCESS_KEY'),
            'secretKey' => config('moredo.SECRET_KEY'),
        ];
        $url  = self::GET_TOKEN_URL;
        try {
            $client      = new GuzzleClient();
            $body        = json_encode($data);
            $response    = $client->request("POST", $url,
                [
                    'body'    => $body,
                    'headers' => ['Content-Type' => 'application/json'],
                    'timeout' => 10
                ]);
            $result      = $response->getBody()->getContents();
            $responseArr = json_decode($result, true);
            Redis::setex(InvoiceCodeEnum::PREFIX_TOKEN . config('moredo.ACCESS_KEY'), 3600, $responseArr['data']);
            return $responseArr['data'];
        } catch (\Exception $exception) {
            throw new InvoiceException(InvoiceCodeEnum::ERR_GET_TOKEN);
        }
    }

    /**
     * 添加税盘
     * @return mixed
     * @throws InvoiceException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public static function addDevice($invoice, $kpjh)
    {
        Log::channel('invoice')->info('web进入添加税盘');
        $token = self::getToken();
        $type  = 0;
        $data  = [
            'token'   => $token,
            'type'    => $type,
            'invoice' => $invoice,
            'kpjh'    => $kpjh
        ];
        $url   = self::ADD_DEVICE_URL;
        try {
            $client   = new GuzzleClient();
            $body     = json_encode($data);
            $response = $client->request("POST", $url,
                [
                    'body'    => $body,
                    'headers' => ['Content-Type' => 'application/json'],
                    'timeout' => 10
                ]);
            $result   = $response->getBody()->getContents();
            Log::channel('invoice')->info('添加税盘结果' . $result);
            $decode = json_decode($result, true);
            if ($decode['ret'] != 200) {
                throw new InvoiceException(InvoiceCodeEnum::ERR_ADD_DEVICE);
            }
        } catch (\Exception $exception) {
            throw new InvoiceException(InvoiceCodeEnum::ERR_ADD_DEVICE);
        }
    }


    /**
     * 申请开票
     * @param $orderId
     * @param $spxx
     * @return mixed
     * @throws InvoiceException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public static function applyInvoice($orderId, $spxx, $invoice, $kpjh)
    {
        $token = self::getToken();
        $data  = [
            'token'   => $token,
            'invoice' => $invoice,
            'kpjh'    => $kpjh,
            'orderId' => $orderId,
            'spxx'    => $spxx
        ];
        $url   = self::APPLY_INVOICE_URL;
        try {
            $client   = new GuzzleClient();
            $body     = json_encode($data);
            $response = $client->request("POST", $url,
                [
                    'body'    => $body,
                    'headers' => ['Content-Type' => 'application/json'],
                    'timeout' => 10
                ]);
            $result   = $response->getBody()->getContents();
            return json_decode($result, true);
        } catch (\Exception $exception) {
            throw new InvoiceException(InvoiceCodeEnum::ERR_APPLY_INVOICE);
        }
    }


    /**
     * 初始化对接信息
     * @param string $name 对接商名称
     * @param string $ipWhiteList ip地址 0.0.0.0为全部，多个用逗号“,”隔开, 如：192.168.0.12, 192.168.0.13
     * @param string $fpUploadUrl 开具发票完成后,将发票数据推送给业务系统请求地址(业务系统方提供)
     * @param string $fpCancelUrl 取消开票后,将命令通知业务系统请求地址(业务系统方提供)
     * @return mixed
     * @throws InvoiceException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public static function init(
        string $name,
        string $ipWhiteList,
        string $fpUploadUrl,
        string $fpCancelUrl,
        string $address,
        string $tel,
        string $contact
    ) {
        $token = self::getToken();
        $data  = [
            'token'       => $token,
            'name'        => $name,
            'fpUploadUrl' => $fpUploadUrl,
            'fpCancelUrl' => $fpCancelUrl,
            'ipWhiteList' => $ipWhiteList,
            'address'     => $address,
            'tel'         => $tel,
            'contact'     => $contact,
        ];
        $url   = self::INIT_URL;
        try {
            $client   = new GuzzleClient();
            $body     = json_encode($data);
            $response = $client->request("POST", $url,
                [
                    'body'    => $body,
                    'headers' => ['Content-Type' => 'application/json'],
                    'timeout' => 10
                ]);
            $result   = $response->getBody()->getContents();
            Log::channel('invoice')->info('盟度初始化结果' . $result);
            $decode = json_decode($result, true);
            if ($decode['ret'] != 200) {
                throw new InvoiceException(InvoiceCodeEnum::ERR_INTI);
            }
        } catch (\Exception $exception) {
            throw new InvoiceException(InvoiceCodeEnum::ERR_INTI);
        }
    }

    /**
     * @param string $gfmc 购方名称，最大长度100
     * @param string $gfsh 购方税号
     * @param string $fplxdm 发票种类: 026电子普票 028电子专票 004纸质专票 007纸质普票
     * @param string $spmc 商品名称，最大长度100
     * @param string $je 金额，保留2位小数
     * @param string $slv 税率，小数格式，如:0.06
     * @param int $hsbz 含税标志(0否，1是)，默认1
     * @param int $spbm 税收分类编码，标准19位
     * @param int $syyhzcbz 优惠政策标志(0否，1是)，默认0
     * @param int $cezsje 差额征收金额，默认0，一般为正数，精确到2位小数
     * @param int $lslbz 零税率标志(0否，1是)，默认0
     * @param int $fphxz 发票行性质: 2被折扣行 1折扣行 0正常行
     * @return string
     */
    public static function getApplyInvoiceSpxxXml(
        string $gfmc,
        string $gfsh,
        string $fplxdm,
        string $spmc,
        string $je,
        string $slv,
        int $hsbz,
        int $spbm,
        int $syyhzcbz = 0,
        int $cezsje = 0,
        int $lslbz = 0,
        int $fphxz = 0
    ) {
        $spxx = ' < business id = "zzsfp" comment = "业务系统单据编号" >
                      <body yylxdm = "1" >
                        <input >
                        <kpxx >
                            <gfmc >%s </gfmc >
                            <gfsh >%s </gfsh >
                            <fplxdm >%s </fplxdm >
                          </kpxx >
                          <fpmx count = "1" >
                           <group xh = "1" >
                              <spmc >%s </spmc >
                              <je >%s </je >
                              <slv >%s </slv >
                              <hsbz >%s </hsbz >
                              <spbm >%s </ spbm >
                              <syyhzcbz >%s </syyhzcbz >
                              <cezsje >%s </cezsje >
                              <lslbz >%s </lslbz >
                              <fphxz >%s </fphxz >
                            </group >
                          </fpmx >
                        </input >
                      </body >
                </business > ';
        return base64_encode(iconv("UTF-8", "GB2312//IGNORE",
            sprintf($spxx, $gfmc, $gfsh, $fplxdm, $spmc, $je, $slv, $hsbz, $spbm, $syyhzcbz, $cezsje,
                $lslbz, $fphxz)));
    }

    /**
     * 扫码开票场景
     * @param $orderId
     * @param $spxx
     * @return mixed
     * @throws InvoiceException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public static function applyInvoiceV2($orderId, $spxx, $invoice, $kpjh)
    {
        $token = self::getToken();
        $data  = [
            'token'   => $token,
            'invoice' => $invoice,
            'kpjh'    => $kpjh,
            'orderId' => $orderId,
            'spxx'    => $spxx
        ];
        $url   = self::RED_INVOICE_V2_URL;
        try {
            $client   = new GuzzleClient();
            $body     = json_encode($data);
            $response = $client->request("POST", $url,
                [
                    'body'    => $body,
                    'headers' => ['Content-Type' => 'application/json'],
                    'timeout' => 10
                ]);
            $result   = $response->getBody()->getContents();
            $decode   = json_decode($result, true);
            Log::channel('invoice')->info("盟度开票回调信息-扫码" . json_encode($decode, JSON_UNESCAPED_UNICODE));
            if ($decode['ret'] != 200) {
                Log::channel("invoice")->info("申请开票失败：" . $orderId . '----' . $decode['msg']);
                throw new InvoiceException(InvoiceCodeEnum::ERR_APPLY_INVOICE, $decode['msg']);
            }
            return $decode;
        } catch (\Exception $exception) {
            Log::channel("invoice")->info("申请开票失败：" . $orderId . '----' . $exception->getMessage());
            throw new InvoiceException(InvoiceCodeEnum::ERR_APPLY_INVOICE, $exception->getMessage());
        }
    }

    /**
     * @param string $fplxdm 发票种类: 026电子普票 028电子专票 004纸质专票 007纸质普票
     * @param string $spmc 商品名称，最大长度100
     * @param string $je 金额，保留2位小数
     * @param string $slv 税率，小数格式，如:0.06
     * @param int $hsbz 含税标志(0否，1是)，默认1
     * @param int $spbm 税收分类编码，标准19位
     * @param int $syyhzcbz 优惠政策标志(0否，1是)，默认0
     * @param int $cezsje 差额征收金额，默认0，一般为正数，精确到2位小数
     * @param int $lslbz 零税率标志(0否，1是)，默认0
     * @param int $fphxz 发票行性质: 2被折扣行 1折扣行 0正常行
     * @return string
     */
    public static function getApplyInvoiceV2SpxxXml(
        string $fplxdm,
        string $spmc,
        string $je,
        string $slv,
        int $hsbz,
        int $spbm,
        int $syyhzcbz = 0,
        string $yhzcsm = '',
        int $cezsje = 0,
        int $lslbz = 0,
        int $fphxz = 0
    ) {
        $spxx = ' < business id = "zzsfp" comment = "业务系统单据编号" >
                  <body yylxdm = "1" >
                    <input >
                      <kpxx >
                        <fplxdm >%s </fplxdm >
                      </kpxx >
                      <fpmx count = "1" >
                       <group xh = "1" >
                          <spmc >%s </spmc >
                          <je >%s </je >
                          <slv >%s </slv >
                          <hsbz >%s </hsbz >
                          <spbm >%s </spbm >
                          <syyhzcbz >%s </syyhzcbz >
                          <yhzcsm >%s </yhzcsm >
                          <cezsje >%s </cezsje >
                          <lslbz >%s </lslbz >
                          <fphxz >%s </fphxz >
                        </group >
                      </fpmx >
                    </input >
                  </body >
                </business > ';
        $spxx = sprintf($spxx, $fplxdm, $spmc, $je, $slv, $hsbz, $spbm, $syyhzcbz, $yhzcsm, $cezsje, $lslbz, $fphxz);
        return base64_encode(iconv("UTF-8", "GB2312//IGNORE",
            sprintf($spxx, $fplxdm, $spmc, $je, $slv, $hsbz, $spbm, $syyhzcbz, $yhzcsm, $cezsje, $lslbz, $fphxz)));
    }


    /**
     * 发票红冲
     * @param string $invoice 纳税人识别号
     * @param string $kpjh 开票机号
     * @param string $orderId 业务系统订单号
     * @param string $fpdm 发票代码
     * @param string $fphm 发票号码
     * @param string $fplxdm 发票类型代码 026电子普票 028电子专票
     * @return mixed
     * @throws InvoiceException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public static function redInvoice(
        string $invoice,
        string $kpjh,
        string $orderId,
        string $fpdm,
        string $fphm,
        string $fplxdm
    ) {
        $token = self::getToken();
        $data  = [
            'token'   => $token,
            'invoice' => $invoice,
            'kpjh'    => $kpjh,
            'fpdm'    => $fpdm,
            'fphm'    => $fphm,
            'fplxdm'  => $fplxdm,
            'orderId' => $orderId
        ];
        $url   = self::RED_INVOICE_URL;
        try {
            $client   = new GuzzleClient();
            $body     = json_encode($data);
            $response = $client->request("POST", $url,
                [
                    'body'    => $body,
                    'headers' => ['Content-Type' => 'application/json'],
                    'timeout' => 10
                ]);
            $result   = $response->getBody()->getContents();
            $decode   = json_decode($result, true);
            if ($decode['ret'] !== 200) {
                throw new \Exception($decode['msg']);
            }
            return $decode;
        } catch (\Exception $exception) {
            Log::channel("invoice")->info("取消开票失败：" . $orderId . '----' . $exception->getMessage());
            throw new InvoiceException(InvoiceCodeEnum::ERR_APPLY_INVOICE);
        }
    }

    /**
     * 取消开票
     * @param $invoice
     * @param $kpjh
     * @param $orderId
     * @return mixed
     * @throws InvoiceException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public static function cancelInvoice($invoice, $kpjh, $orderId)
    {
        $token = self::getToken();
        $data  = [
            'token'   => $token,
            'invoice' => $invoice,
            'kpjh'    => $kpjh,
            'orderId' => $orderId
        ];
        $url   = self::CANCEL_INVOICE_URL;
        try {
            $client   = new GuzzleClient();
            $body     = json_encode($data);
            $response = $client->request("POST", $url,
                [
                    'body'    => $body,
                    'headers' => ['Content-Type' => 'application/json'],
                    'timeout' => 10
                ]);
            $result   = $response->getBody()->getContents();
            $decode   = json_decode($result, true);
            if ($decode['ret'] !== 200) {
                throw new \Exception($decode['msg']);
            }
            return $decode;
        } catch (\Exception $exception) {
            Log::channel("invoice")->info("取消开票失败：" . $orderId . '----' . $exception->getMessage());
            throw new InvoiceException(InvoiceCodeEnum::ERR_CANCEL);
        }
    }
}
